using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Frogger
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager __graphics;
        SpriteBatch __spriteBatch;
        
        //Manager to control all the updates and draws of the srpites
        LevelOneManager __manager;

        //Audio
        AudioEngine __audioEngine;
        WaveBank __waveBank;
        SoundBank __soundBank;
        Cue __trackCue;

        //Random
        public Random rnd { get; private set; }

        //Scoring
        int __currentScore = 0;
        int __greenBugCount = 0;

        SpriteFont __scoreFont;

        //Game states
        enum GameState { Start, Help, Exit, InGame, Pause, GameOver };
        GameState __currentGameState = GameState.Start;
            //Arrow
        ArrowSprite __arrow;
            //Start Menu list
        List<Entity> __choiceList = new List<Entity>();
            //Pause Menu List
        List<Entity> __pauseChoiceList = new List<Entity>();
            //Button states
        bool[] __isButtonClicked = { false, false, false, false };
        bool[] __pauseIsButtonClicked = { false, false };


        public Game1()
        {
            __graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            //Initialize random variable
            rnd = new Random();

            //Set game window size
            __graphics.PreferredBackBufferHeight = 768;
            __graphics.PreferredBackBufferWidth = 1024;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            __manager = new LevelOneManager(this);
            Components.Add(__manager);
            //Sprite manager should be disabled at start
            __manager.Enabled = false;
            __manager.Visible = false;

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            __spriteBatch = new SpriteBatch(GraphicsDevice);

            //Load audio objects
            __audioEngine = new AudioEngine(@"Content\Audio\GameAudio.xgs");
            __waveBank = new WaveBank(__audioEngine, @"Content\Audio\Wave Bank.xwb");
            __soundBank = new SoundBank(__audioEngine, @"Content\Audio\Sound Bank.xsb");

            // Start the background audio
            __trackCue = __soundBank.GetCue("Before Dawn");

            //Load fonts
            __scoreFont = Content.Load<SpriteFont>(@"fonts\arial");

            //Load sprites for arrow
            this.__arrow = new ArrowSprite(
                Content.Load<Texture2D>(@"Images/Menu/arrow"),
                new Vector2(Window.ClientBounds.Width / 2,
                  Window.ClientBounds.Height / 2),
                new Point(30, 30), 2, new Point(0, 0),
                new Point(2, 1), Vector2.Zero, 1.0f);
            //Load options on welcome screen
            __choiceList.Add(new OptionSprite(
                    Content.Load<Texture2D>(@"Images\Menu\Title"),
                    new Vector2(345, 145), new Point(300, 60), 2,
                    new Point(0, 0), new Point(1, 1), Vector2.Zero,
                    null, 0));
            __choiceList.Add(new OptionSprite(
                    Content.Load<Texture2D>(@"Images\Menu\Start"),
                    new Vector2(420, 225), new Point(150, 60), 2,
                    new Point(0, 0), new Point(1, 1), Vector2.Zero,
                    null, 0));
            __choiceList.Add(new OptionSprite(
                    Content.Load<Texture2D>(@"Images\Menu\Help"),
                    new Vector2(420, 310), new Point(150, 60), 2,
                    new Point(0, 0), new Point(1, 1), Vector2.Zero,
                    null, 0));
            __choiceList.Add(new OptionSprite(
                    Content.Load<Texture2D>(@"Images\Menu\Exit"),
                    new Vector2(420, 390), new Point(150, 60), 2,
                    new Point(0, 0), new Point(1, 1), Vector2.Zero,
                    null, 0));
            //Load options on pause screen
            __pauseChoiceList.Add(new OptionSprite(
                    Content.Load<Texture2D>(@"Images\Menu\Resume"),
                    new Vector2(395, 175), new Point(250, 60), 2,
                    new Point(0, 0), new Point(1, 1), Vector2.Zero,
                    null, 0));
            __pauseChoiceList.Add(new OptionSprite(
                    Content.Load<Texture2D>(@"Images\Menu\End"),
                    new Vector2(370, 255), new Point(300, 60), 2,
                    new Point(0, 0), new Point(1, 1), Vector2.Zero,
                    null, 0));
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Only perform certain actions based on
            // the current game state
            switch (__currentGameState)
            {
                case GameState.Start:
                    {
                        // Update player
                        __arrow.Update(gameTime, Window.ClientBounds);

                        // Update all non-arrow sprites
                        for (int i = 0; i < __choiceList.Count; ++i)
                        {
                            Entity s = __choiceList[i];
                            s.Update(gameTime, Window.ClientBounds);

                            // Check for collisions
                            if (s.collisionRect.Intersects(__arrow.collisionRect))
                            {
                                MouseState currMouseState = Mouse.GetState();
                               
                                switch (i)
                                {
                                    case 1:
                                        {
                                            __arrow.__currentArrowState = ArrowSprite.ArrowState.Selected;
                                            if (currMouseState.LeftButton == ButtonState.Pressed)
                                            {
                                                __currentGameState = GameState.InGame;
                                                __trackCue.Play();
                                                __manager.Enabled = true;
                                                __manager.Visible = true;
                                            }
                                            if (!__isButtonClicked[i])
                                            {
                                                __isButtonClicked[i] = true;
                                            }
                                            break;
                                        }
                                    case 2:
                                        {
                                            __arrow.__currentArrowState = ArrowSprite.ArrowState.Selected;
                                            if (currMouseState.LeftButton == ButtonState.Pressed)
                                            {
                                                __currentGameState = GameState.Help;
                                            }
                                            if (!__isButtonClicked[i])
                                            {
                                                __isButtonClicked[i] = true;
                                            }
                                            break;
                                        }
                                    case 3:
                                        {
                                            __arrow.__currentArrowState = ArrowSprite.ArrowState.Selected;
                                            if (currMouseState.LeftButton == ButtonState.Pressed)
                                            {
                                                __currentGameState = GameState.Exit;
                                            }
                                            if (!__isButtonClicked[i])
                                            {
                                                __isButtonClicked[i] = true;
                                            }
                                            break;
                                        }
                                }
                                break;
                            }
                            else
                            {
                                for (int k = 0; k < __choiceList.Count; ++k)
                                {
                                    if (__isButtonClicked[k])
                                    {
                                        __arrow.__currentArrowState = ArrowSprite.ArrowState.Normal;
                                        for (int j = 0; j < __choiceList.Count; ++j)
                                        {
                                            __isButtonClicked[j] = false;
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                        break;
                    }
                case GameState.Help:
                    {
                        if (Keyboard.GetState().IsKeyDown(Keys.Enter))
                        {
                            __currentGameState = GameState.Start;
                        }
                        break;
                    }

                case GameState.Exit:
                    {
                        if (Keyboard.GetState().IsKeyDown(Keys.Enter))
                        {
                            Exit();
                        }
                        break;
                    }

                case GameState.Pause:
                    {
                        __arrow.Update(gameTime, Window.ClientBounds);
                        __manager.Enabled = false;
                        __manager.Visible = false;
                        __trackCue.Pause();

                        // Update all non-player sprites
                        for (int i = 0; i < __pauseChoiceList.Count; ++i)
                        {
                            Entity s = __pauseChoiceList[i];
                            s.Update(gameTime, Window.ClientBounds);

                            // Check for collisions
                            if (s.collisionRect.Intersects(__arrow.collisionRect))
                            {
                                MouseState currMouseState = Mouse.GetState();
                                __arrow.__currentArrowState = ArrowSprite.ArrowState.Selected;
                                switch (i)
                                {
                                    case 0:
                                        {
                                            if (!__pauseIsButtonClicked[i])
                                            {
                                                __pauseIsButtonClicked[i] = true;
                                            }
                                            if (currMouseState.LeftButton == ButtonState.Pressed)
                                            {
                                                __manager.Enabled = true;
                                                __manager.Visible = true;
                                                __trackCue.Resume();
                                                __currentGameState = GameState.InGame;
                                            }
                                            break;
                                        }
                                    case 1:
                                        {
                                            if (!__pauseIsButtonClicked[i])
                                            {
                                                __pauseIsButtonClicked[i] = true;
                                            }
                                            if (currMouseState.LeftButton == ButtonState.Pressed)
                                            {
                                                Exit();
                                            }
                                            break;
                                        }
                                }
                                break;
                            }
                            else
                            {

                                for (int k = 0; k < __pauseChoiceList.Count; ++k)
                                {
                                    if (__pauseIsButtonClicked[k])
                                    {
                                        __arrow.__currentArrowState = ArrowSprite.ArrowState.Normal;
                                        for (int j = 0; j < __pauseChoiceList.Count; ++j)
                                        {
                                            __pauseIsButtonClicked[j] = false;
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                        break;
                    }
                case GameState.InGame:
                    {
                        if (__trackCue.IsStopped)
                        {
                            __manager.Enabled = false;
                            __manager.Visible = false;
                            __currentGameState = GameState.GameOver;
                        }
                        if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                        {
                            __currentGameState = GameState.Pause;
                        }
                        break;
                    }
                case GameState.GameOver:
                    {
                        if (Keyboard.GetState().IsKeyDown(Keys.Enter))
                        {
                            Exit();
                        }
                        break;
                    }
            }

            //Update audio
            __audioEngine.Update();
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            // Only draw certain items based on
            // the current game state
            switch (__currentGameState)
            {
                case GameState.Start:
                    {
                        GraphicsDevice.Clear(Color.Black);

                        // Draw text for intro splash screen
                        __spriteBatch.Begin();

                        foreach (OptionSprite s in __choiceList)
                        {
                            s.Draw(gameTime, __spriteBatch);
                        }

                        __arrow.Draw(gameTime, __spriteBatch);
                        
                        __spriteBatch.End();
                        break;
                    }
                case GameState.Help:
                    {
                        GraphicsDevice.Clear(Color.Black);
                        // Draw text for intro splash screen
                        __spriteBatch.Begin();
                        // Draw the texts
                        string text = "Avoid the cars or lose score!";
                        __spriteBatch.DrawString(__scoreFont, text,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(text).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(text).Y / 3)),
                                Color.White);

                        text = "Catch yellow bugs to add score. You will get fatter if you eat too much~LOL";
                        __spriteBatch.DrawString(__scoreFont, text,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(text).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(text).Y / 3) + 30),
                                Color.White);

                        text = "Catch green hearts to accumulate additional score.";
                        __spriteBatch.DrawString(__scoreFont, text,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(text).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(text).Y / 3) + 60),
                                Color.White);

                        text = "Super powers are offered!";
                        __spriteBatch.DrawString(__scoreFont, text,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(text).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(text).Y / 3) + 90),
                                Color.White);

                        text = "(Press Enter to go back to Menu)";
                        __spriteBatch.DrawString(__scoreFont, text,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(text).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(text).Y / 3) + 120),
                                Color.White);

                        __spriteBatch.End();
                        break;
                    }
                case GameState.Exit:
                    {
                        GraphicsDevice.Clear(Color.Black);
                        // Draw text for intro splash screen
                        __spriteBatch.Begin();

                        string exit_gameover = "(Press ENTER to Exit)";
                        __spriteBatch.DrawString(__scoreFont, exit_gameover,
                            new Vector2(((Window.ClientBounds.Width / 2) + 300)
                            - (__scoreFont.MeasureString(exit_gameover).X / 2),
                            ((Window.ClientBounds.Height / 2) + 170)
                            - (__scoreFont.MeasureString(exit_gameover).Y / 2) + 60),
                            Color.SaddleBrown);

                        __spriteBatch.End();
                        break;
                    }
                case GameState.Pause:
                    {
                        GraphicsDevice.Clear(Color.Black);
                        __spriteBatch.Begin();
                        // Draw the player

                        foreach (OptionSprite s in __pauseChoiceList)
                        {
                            s.Draw(gameTime, __spriteBatch);
                        }

                        __arrow.Draw(gameTime, __spriteBatch);
                        __spriteBatch.End();
                        break;
                    }
                case GameState.InGame:
                    {
                        GraphicsDevice.Clear(Color.Black);
                        __spriteBatch.Begin();

                        // Draw background 

                        // Draw fonts
                        __spriteBatch.DrawString(__scoreFont,
                            "Score: " + __currentScore,
                            new Vector2(10, 10), Color.White,
                            0, Vector2.Zero,
                            1, SpriteEffects.None, 1);

                        // GreenBugCount
                        __spriteBatch.DrawString(__scoreFont,
                            "Green Accumulation: " + __greenBugCount,
                            new Vector2(780, 10), Color.AliceBlue,
                            0, Vector2.Zero,
                            1, SpriteEffects.None, 1);

                        __spriteBatch.End();

                        break;
                    }
                case GameState.GameOver:
                    {
                        GraphicsDevice.Clear(Color.Black);

                        //Draw game over text
                        __spriteBatch.Begin();
                        string gameover = "Game Over!";
                        __spriteBatch.DrawString(__scoreFont, gameover,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(gameover).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(gameover).Y / 2)),
                            Color.SaddleBrown);

                        gameover = "Your score: " + __currentScore;
                        __spriteBatch.DrawString(__scoreFont, gameover,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(gameover).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(gameover).Y / 2) + 30),
                            Color.SaddleBrown);

                        gameover = "Your Green Heart counts: " + __greenBugCount;
                        __spriteBatch.DrawString(__scoreFont, gameover,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(gameover).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(gameover).Y / 2) + 60),
                            Color.SaddleBrown);

                        gameover = "(Press ENTER to exit)";
                        __spriteBatch.DrawString(__scoreFont, gameover,
                            new Vector2((Window.ClientBounds.Width / 2)
                                - (__scoreFont.MeasureString(gameover).X / 2),
                                (Window.ClientBounds.Height / 2)
                                - (__scoreFont.MeasureString(gameover).Y / 2) + 90),
                        Color.SaddleBrown);

                        __spriteBatch.End();
                        break;
                    }
            }

            base.Draw(gameTime);
        }

        public void PlayCue(string cueName)
        {
            __soundBank.PlayCue(cueName);
        }

        public void AddScore(int score)
        {
            __currentScore += score;
        }

        public void AddGreenBug()
        {
            __greenBugCount += 1; 
        }

        public void ReduceGreenBug()
        {
            __greenBugCount -= 3;
            if (__greenBugCount < 4)
            {
                __greenBugCount = 0;
            }
        }

    }
}
